#include"os.h"

typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;

//task0 LDT表结构体
struct {uint16_t limitLow, baseLow, baseHAndAttr, baseHAndlimitH;} task0LDT[256] __attribute__((aligned(8))) = {
    [TASK_CODE_SEG / GDT_ELEMENT_BYTE_SIZES] = {0Xffff, 0x0000, 0xfa00, 0x00cf},//c:1100
    [TASK_DATA_SEG / GDT_ELEMENT_BYTE_SIZES] = {0Xffff, 0x0000, 0xf300, 0x00cf},
};

//task1 LDT表结构体
struct {uint16_t limitLow, baseLow, baseHAndAttr, baseHAndlimitH;} task1LDT[256] __attribute__((aligned(8))) = {
    [TASK_CODE_SEG / GDT_ELEMENT_BYTE_SIZES] = {0Xffff, 0x0000, 0xfa00, 0x00cf},//c:1100
    [TASK_DATA_SEG / GDT_ELEMENT_BYTE_SIZES] = {0Xffff, 0x0000, 0xf300, 0x00cf},
};

//gdt表结构体,八字节对齐 —— 保护模式
struct {uint16_t limitLow, baseLow, baseHAndAttr, baseHAndlimitH;} gdtTable[256] __attribute__((aligned(8))) = {
    // 1111 1111 1111 1111, 0000 0000 0000 0000, 1001 1010 0000 0000, 0000 0000 1100 1111
    [KERNEL_CODE_SEG / GDT_ELEMENT_BYTE_SIZES] = {0xffff, 0x0000, 0x9a00, 0x00cf},
    [KERNEL_DATA_SEG / GDT_ELEMENT_BYTE_SIZES] = {0Xffff, 0x0000, 0x9200, 0x00cf},
    [APP_CODE_SEG / GDT_ELEMENT_BYTE_SIZES] = {0Xffff, 0x0000, 0xfa00, 0x00cf},  // 
    [APP_DATA_SEG / GDT_ELEMENT_BYTE_SIZES] = {0Xffff, 0x0000, 0xf300, 0x00cf},
    [TASK0_TSS_SEG / GDT_ELEMENT_BYTE_SIZES] = {0x68, 0x0, 0xe900, 0x0},
    [TASK1_TSS_SEG / GDT_ELEMENT_BYTE_SIZES] = {0x68, 0x0, 0xe900, 0x0},
    [SYSCALL_SEG / GDT_ELEMENT_BYTE_SIZES] = {0x0000, KERNEL_CODE_SEG, 0xec03, 0x0000}, // e:1110;8:1000
    [TASK0_LDT_SEG / GDT_ELEMENT_BYTE_SIZES] = {sizeof(task0LDT) - 1, 0x0, 0xe200, 0x00cf},
    [TASK1_LDT_SEG / GDT_ELEMENT_BYTE_SIZES] = {sizeof(task1LDT) - 1, 0x0, 0xe200, 0x00cf},
};

#define PDE_P   (1 << 0)
#define PDE_W   (1 << 1) //读写权限
#define PDE_U   (1 << 2) //权限设置,是否可被低特权程序写入
#define PDE_PS  (1 << 7) //4MB页表

// 1111 0000 00 
// 31-22:一级页表项,21-12:二级页表项,11-0:物理页偏移地址
#define NEED_ADDR 0xf0000000 //假设这个是程序需要的地址，对此进行分页设置


///////////////

//一级页表设置
uint32_t pgDir[1024] __attribute__((aligned(4096))) = {
    [0] = PDE_P | PDE_W | PDE_U | PDE_PS | 0  //第一页恒等映射物理
} ;

//二级页表设置
static uint32_t pageTable[1024] __attribute__((aligned(4096))) = {PDE_U};

//二级页表每个元素对应的内存快内容（4KB）
uint8_t pagePhyBuffer[4096] __attribute__((aligned(4096))) = {0x36};


//C语言调用 outb %al,(%dx)
void outb (uint8_t data, uint16_t port)
{
    __asm__ __volatile__("outb %[v], %[p]"::[p]"d"(port), [v]"a"(data));
}

//IDT表结构体,八字节对齐 (Interrupt Descriptor Table)
struct {uint16_t offsetLow, segmentSelector, attr, offsetHigh;} idtTable[256] __attribute__((aligned(8)));

void task_sched(void) {
    
    static int taskTss = TASK0_TSS_SEG;
    static int taskLdt = TASK0_LDT_SEG;

    taskTss = (taskTss == TASK0_TSS_SEG) ? TASK1_TSS_SEG : TASK0_TSS_SEG;
    taskLdt = (taskLdt == TASK0_LDT_SEG) ? TASK1_LDT_SEG : TASK0_LDT_SEG;

    uint32_t addr[] = {0, taskTss};
    __asm__ __volatile__("lldt %[ldt]"::[ldt]"m"(taskLdt));
    __asm__ __volatile__("ljmpl *(%[a])"::[a]"r"(addr)); //此时会将正在运行的任务状态保存到其相应的tss里面,跳转到新的tss运行，运行的位置是tss里存储的eip寄存器存储的位置
}

void do_system_call(int type, char* str, uint8_t color)
{
    static int row = 1;
    if (type == 2) {
        uint16_t* dest = (uint16_t*)0x000b8000 + 80 * row;
        while (*str) {
            *dest = *str | ((uint16_t)color << 8);
            dest++;
            str++;
        }
        row++;
        if (row >= 25) {
            row = 1;
        }
        for (int i = 0; i < 0xFFFFF; i++);
    }
}

//程序直接调用的字符串输出函数
void system_show(char* str, uint8_t color) {
     uint32_t addr[] = {0, SYSCALL_SEG}; //系统调用门
     __asm__ __volatile__("push %[color];push %[str];push %[id];lcalll *(%[a])" 
        ::[a]"r"(addr), [color]"m"(color), [str]"m"(str), [id]"r"(2));
    //do_system_call(2, str, color);
}

//系统调用函数
void syscall_output(void);


//task0栈空间
uint32_t task0Dpl3Stack[1024], task0Dpl0Stack[1024];
//task1栈空间
uint32_t task1Dpl3Stack[1024], task1Dpl0Stack[1024];



void task0 (void) 
{
    char* str = "task 1";
    uint8_t color = 0;

    /**
     * 这里有一个思考，就是处于特权等级3的task0能否访问这个地址：
     * 因为开启了分页机制，转换后发现是处于一级页表第一页，和物理地址是恒等映射；
     * 再次查看其设置的权限，发现用户态（特权3）可以正常访问，由此，此处可以正常访问该地址的内容并修改
     * uint16_t* dest = (uint16_t*)0x000b8000
     */
    for(;color<=0xff;) {
        system_show(str, color++);
    }
}

void task1 (void) 
{
    char* str = "task 2";
    uint8_t color = 0xff;
    for(;color>=0x0;) {
        system_show(str, color--);
    }
}

//任务0的任务状态段(task state segment----tss)
uint32_t task0Tss[] = {
    // prelink, esp0, ss0, esp1, ss1, esp2, ss2
    0,  (uint32_t)task0Dpl0Stack + 4*1024, KERNEL_DATA_SEG , /* 后边不用使用 */ 0x0, 0x0, 0x0, 0x0,
    // cr3, eip, eflags, eax, ecx, edx, ebx, esp, ebp, esi, edi,
    (uint32_t)pgDir,  (uint32_t)task0/*入口地址*/, 0x202, 0xa, 0xc, 0xd, 0xb, (uint32_t)task0Dpl3Stack + 4*1024/* 栈 */, 0x1, 0x2, 0x3,
    // es, cs, ss, ds, fs, gs, ldt, iomap
    TASK_DATA_SEG, TASK_CODE_SEG, TASK_DATA_SEG, TASK_DATA_SEG, TASK_DATA_SEG, TASK_DATA_SEG, TASK0_LDT_SEG, 0x0,
};
//任务1的任务状态段(task state segment----tss)
uint32_t task1Tss[] = {
    // prelink, esp0, ss0, esp1, ss1, esp2, ss2
    0,  (uint32_t)task1Dpl0Stack + 4*1024, KERNEL_DATA_SEG , /* 后边不用使用 */ 0x0, 0x0, 0x0, 0x0,
    // cr3, eip, eflags, eax, ecx, edx, ebx, esp, ebp, esi, edi,
    (uint32_t)pgDir,  (uint32_t)task1/*入口地址*/, 0x202, 0xa, 0xc, 0xd, 0xb, (uint32_t)task1Dpl3Stack + 4*1024/* 栈 */, 0x1, 0x2, 0x3,
    // es, cs, ss, ds, fs, gs, ldt, iomap
    TASK_DATA_SEG, TASK_CODE_SEG, TASK_DATA_SEG, TASK_DATA_SEG, TASK_DATA_SEG, TASK_DATA_SEG, TASK1_LDT_SEG, 0x0,
};

//中断函数声明
void time_interrupt(void);

//初始化一级页表和二级页表
void os_init()
{   
    //初始化主从8259
    outb(0x11, 0x20);
    outb(0x11, 0xa0);

    outb(0x20, 0x21);
    outb(0x28, 0xa1);

    outb(1 << 2, 0x21);
    outb(2, 0xa1);

    outb(0x1, 0x21);
    outb(0x1, 0xa1);

    outb(0xfe, 0x21); //只打开主芯片R0中断 1111 1110
    outb(0xff, 0xa1); //关闭从芯片全部中断

    int tmo = 1193180 / 100; //中断时间100ms
    outb(0x36, 0x43);
    outb((uint8_t)tmo, 0x40);
    outb((uint8_t)(tmo>>8), 0x40);

    idtTable[0x20].offsetLow = (uint32_t)time_interrupt & 0xffff;
    idtTable[0x20].offsetHigh = (uint32_t)time_interrupt >> 16;
    idtTable[0x20].segmentSelector = (uint16_t)(KERNEL_CODE_SEG); //代码段选择子
    idtTable[0x20].attr = 0x8E00;

    gdtTable[TASK0_TSS_SEG / GDT_ELEMENT_BYTE_SIZES].baseLow = (uint16_t)(uint32_t)task0Tss;
    gdtTable[TASK1_TSS_SEG / GDT_ELEMENT_BYTE_SIZES].baseLow = (uint16_t)(uint32_t)task1Tss;
    gdtTable[SYSCALL_SEG / GDT_ELEMENT_BYTE_SIZES].limitLow = (uint16_t)(uint32_t)syscall_output;
    
    gdtTable[TASK0_LDT_SEG / GDT_ELEMENT_BYTE_SIZES].baseLow = (uint32_t)task0LDT & 0xffff;
    gdtTable[TASK0_LDT_SEG / GDT_ELEMENT_BYTE_SIZES].baseHAndAttr |= ((uint32_t)task0LDT >> 16) & 0x00ff;
    gdtTable[TASK0_LDT_SEG / GDT_ELEMENT_BYTE_SIZES].baseHAndlimitH |= ((uint32_t)task0LDT >> 16) & 0xff00;

    gdtTable[TASK1_LDT_SEG / GDT_ELEMENT_BYTE_SIZES].baseLow = (uint32_t)task1LDT & 0xffff;
    gdtTable[TASK1_LDT_SEG / GDT_ELEMENT_BYTE_SIZES].baseHAndAttr |= ((uint32_t)task1LDT >> 16) & 0x00ff;
    gdtTable[TASK1_LDT_SEG / GDT_ELEMENT_BYTE_SIZES].baseHAndlimitH |= ((uint32_t)task1LDT >> 16) & 0xff00;

    pgDir[NEED_ADDR >> 22] = PDE_P | PDE_W | PDE_U | (uint32_t)pageTable;
    pageTable[(NEED_ADDR >> 12) & 0x3ff] = PDE_P | PDE_W | PDE_U | (uint32_t)pagePhyBuffer;
}




